#include "filesystem/be_filesystem.h"
#include "settings.h"

Settings* Settings::Instance () 
{
	static Settings t;
	return &t;
}

Settings::Settings()
{
	profileName			= "default";

	registerCVar("fullscreen",						0, 0, 1, true, "enable fullscreen mode");
	registerCVar("headless",						0, 0, 1, true, "do not open gl context");
	registerCVar("drawscene",						1, 0, 1, true, "draw the scene");
	registerCVar("fsX",							800, 1, 1000000, false, "fullscreen resolution X");
	registerCVar("fsY",							600, 1, 1000000, false, "fullscreen resolution Y");
	registerCVar("fpslimit_frames",						60, 1, 1000, false, "frames per second for the fps limiter");
	registerCVar("fpslimit",						1, 0, 1, true, "enable fps limiter");

	//New window settings
	registerCVar("window_safemode", 0, 0, 1, true, "enable safemode window settings");
	registerCVar("window_width", 1024, 640, 9999, false, "window client width in pixels");
	registerCVar("window_height", 768, 480, 9999, false, "window client height in pixels");
	registerCVar("window_color_bits", 24, 16, 32, false, "window framebuffer color bits per pixel");
	registerCVar("window_depth_bits", 24, 8, 32, false, "window framebuffer depth bits per pixel");
	registerCVar("window_stencil_bits", 0, 0, 32, false, "window framebuffer stencil bits per pixel");
	registerCVar("window_multisamples", 0, 0, 16, false, "window framebuffer multisamples");
	registerCVar("window_doublebuffer", 1, 0, 1, true, "double buffered window");
	registerCVar("window_vsync", 0, 0, 1, true, "vsynced window");
	registerCVar("window_resizable", 0, 0, 1, true, "enable resizable window");
	registerCVar("window_fullscreen", 0, 0, 1, true, "enable fullscreen window");
	registerCVar("window_hardware_gl", 1, 0, 1, true, "enable hardware GL window");

	dirlayout = Dirlayout::Instance();
	
	pthread_mutex_init (&settings_m, NULL);		// init settings mutex
}

void Settings::registerCVar(const std::string& name, const std::string& value, const std::string& comment)
{
	cvar* v = new cvar();

	v->type = T_STRING;
	v->string_val = value;
	v->comment = comment;

	unregisterCVar(name);
	cvarlist[name] = v;

// 	cvarlist[name]->loop = true;
}

void Settings::registerCVar(const std::string& name, const unsigned int& defaultvalue, const unsigned int& min_val, const unsigned int& max_val, const std::string& comment)
{
	registerCVar( name, defaultvalue, min_val, max_val, false, comment );
}

void Settings::registerCVar(const std::string& name, const unsigned int& defaultvalue, const unsigned int& min_val, const unsigned int& max_val, bool loop, const std::string& comment)
{
	cvar* v = new cvar();

	v->type = T_INT;
	v->int_val = defaultvalue;
	v->int_min = min_val;
	v->int_max = max_val;
	v->loop = loop;
	v->comment = comment;

	unregisterCVar(name);
	cvarlist[name] = v;

// 	cvarlist[name]->loop = true;
}

void Settings::registerLocalCVar(const std::string& name, const unsigned int& defaultvalue, const unsigned int& min_val, const unsigned int& max_val, bool loop, const std::string& comment)
{
	cvar* v = new cvar();

	v->type = T_INT;
	v->int_val = defaultvalue;
	v->int_min = min_val;
	v->int_max = max_val;
	v->loop = loop;
	v->local = true;
	v->comment = comment;

	unregisterCVar(name);
	cvarlist[name] = v;

// 	cvarlist[name]->loop = true;
}

void Settings::registerLocalCVar(const std::string& name, const std::string& value, const std::string& comment)
{
	cvar* v = new cvar();

	v->type = T_STRING;
	v->string_val = value;
	v->comment = comment;
	v->local = true;

	unregisterCVar(name);
	cvarlist[name] = v;

// 	cvarlist[name]->loop = true;
}

unsigned int Settings::getCVar(const std::string& name)
{
	// the fast"er" way
	return cvarlist[name]->int_val;
	
// 	// the slow way
// 	cvarit = cvarlist.find(name);
// 	if ( cvarit != cvarlist.end() )
// 		return cvarit->second->int_val;
// 	else 
// 		std::cerr << "getCVar: no such key: " << name << std::endl;
// 
// 	return 0;
}

const std::string& Settings::getCVarS(const std::string& name)
{
	// the fast"er" way
	return cvarlist[name]->string_val;
	
// 	// the slow way
// 	cvarit = cvarlist.find(name);
// 	if ( cvarit != cvarlist.end() )
// 		return cvarit->second->int_val;
// 	else 
// 		std::cerr << "getCVar: no such key: " << name << std::endl;
// 
// 	return 0;
}

const unsigned int* Settings::getCVarPtr(const std::string& name)
{
	return &cvarlist[name]->int_val;
}

bool Settings::setCVar(const std::string& name, const unsigned int& value)
{
	if ( value >= cvarlist[name]->int_min && value <= cvarlist[name]->int_max )
	{
		cvarlist[name]->int_val = value;
		return true;
	}
	else
	{
		std::cerr << name << " expects a value that is >=" << cvarlist[name]->int_min << " and <=" << cvarlist[name]->int_max << std::endl;
		return false;
	}
}

bool Settings::setCVar(const std::string& name, const std::string& strvalue)
{
	if ( cvarlist[name]->type == T_INT )
	{
		unsigned int value = atoi( strvalue.c_str() );
		if ( value >= cvarlist[name]->int_min && value <= cvarlist[name]->int_max )
		{
			cvarlist[name]->int_val = value;
			return true;
		}
		else
		{
			std::cerr << name << " expects a value that is >=" << cvarlist[name]->int_min << " and <=" << cvarlist[name]->int_max << std::endl;
			return false;
		}
	}
	else if ( cvarlist[name]->type == T_STRING )
	{
		cvarlist[name]->string_val = strvalue;
		return true;
	}

	return false;
}

// FIXME private
void Settings::increaseCVar(const std::string& name, const unsigned int& value)
{
	unsigned int diff = cvarlist[name]->int_val + value;
	if ( diff >= cvarlist[name]->int_min && diff <= cvarlist[name]->int_max )
		cvarlist[name]->int_val += value;
	else if ( cvarlist[name]->loop )
	{
		cvarlist[name]->int_val = cvarlist[name]->int_min;
	}
}

// FIXME private
void Settings::decreaseCVar(const std::string& name, const unsigned int& value)
{
	unsigned int diff = cvarlist[name]->int_val - value;
	if ( diff >= cvarlist[name]->int_min && diff <= cvarlist[name]->int_max )
		cvarlist[name]->int_val -= value;
}

void Settings::increaseCVar(const std::string& name)
{
	unsigned int diff = cvarlist[name]->int_val + 1;
	if ( diff >= cvarlist[name]->int_min && diff <= cvarlist[name]->int_max )
		cvarlist[name]->int_val = diff;
	else if ( cvarlist[name]->loop )
	{
		cvarlist[name]->int_val = cvarlist[name]->int_min;
	}
	std::stringstream buf;
	buf << name << ": " << getCVar(name);
	Logbuffer::Instance()->add(buf);
}

void Settings::decreaseCVar(const std::string& name)
{
	int diff = -1 + cvarlist[name]->int_val;
	if ( diff >= (int)cvarlist[name]->int_min && diff <= (int)cvarlist[name]->int_max )
		cvarlist[name]->int_val = (unsigned int)diff;
	std::stringstream buf;
	buf << name << ": " << getCVar(name);
	Logbuffer::Instance()->add(buf);
}

void Settings::unregisterCVar(const std::string& name)
{
	cvarit = cvarlist.find(name);
	if ( cvarit != cvarlist.end() )
	{
		delete cvarit->second;
		cvarlist.erase(cvarit->first);
	}
}

bool Settings::isCVar(const std::string& name)
{
	cvarit = cvarlist.find(name);
	if ( cvarit != cvarlist.end() )
		return true;

	return false;
}

void Settings::createHelpInfo()
{
// 	helpinfo << "STARTUP OPTIONS" << std::endl << std::endl;
// 	helpinfo << "  Global Settings" << std::endl;

	helpinfo << std::endl << "  option                                                    [default] [range]      [comment]" << std::endl << std::endl;
	for( cvarit = cvarlist.begin(); cvarit != cvarlist.end(); cvarit++ )
	{
		std::stringstream buf("");
		buf << "  --" << cvarit->first;
// 		buf << " " << buf.str().size();
		
		for ( unsigned int i=buf.str().size(); i < 62; i++ )
			buf << " ";
		
		if ( cvarit->second->type == T_INT )
		{
			buf << cvarit->second->int_val;

			for ( unsigned int i=buf.str().size(); i < 72; i++ )
				buf << " ";

			buf << cvarit->second->int_min << "-" << cvarit->second->int_max;
		}
		else if ( cvarit->second->type == T_STRING )
		{
			buf << cvarit->second->string_val;
		}

		for ( unsigned int i=buf.str().size(); i < 85; i++ )
			buf << " ";

		buf << cvarit->second->comment;

		helpinfo << buf.str() << std::endl;
	}

	helpinfo << std::endl << " To save the default settings to a profile, press \"s\" in the simulation.  \n It will be saved to ./default and can be loaded by using \"--profile ./default\"" << std::endl ;
	helpinfo << std::endl << " Use F1 in the simulation for more information about keys." << std::endl;

}

void Settings::loadProfile(char* filename)
{
	std::cerr << "loading settins from '" << filename << "'" << std::endl;

	// nasty code for stripping dir names from the given profile name
	profileName = filename;
	std::string dirs("");
	parseH.beginMatchesStrip( "/", profileName );
	dirs = parseH.returnUntillStrip( "/", profileName );
	while ( !dirs.empty() )
	{
		dirs="";
		dirs = parseH.returnUntillStrip( "/", profileName );
	}
	profileName = parseH.returnUntillStrip( ".pro", profileName );

	BeFile befileProfile;
	
	
// 	string content;
// 	if ( fileH.open( filename, content ) )
	if ( BeFilesystem::Instance()->load( befileProfile, filename ) )
	{
// 		content.append("\n");
// 		string line = parseH.returnUntillStrip( "\n", content );
		std::string line;

// 		while ( !content.empty() )
		while ( befileProfile.getLine(line) )
		{
			// trim spaces
			while ( parseH.beginMatchesStrip( " ", line ) ) {};

			// remove comment lines
			if ( parseH.beginMatchesStrip( "#", line ) )
				line.clear();

			if ( !line.empty() )
			{
				std::string sw = parseH.returnUntillStrip( " ", line );
				if ( !sw.empty() )
				{
					if ( isCVar(sw) )
					{
						while ( parseH.beginMatchesStrip( " ", line ) ) {};
						if ( !line.empty() )
						{
							if ( !setCVar(sw, line) )
								exit(1);
							line.clear();
						}
						else
						{
							std::cerr << "Option expects an argument: " << sw << std::endl;
							exit(1);
						}

					}
					else
					{
						std::cerr << "Unknown option in profile: " << sw << std::endl;
					}
				}
				else
				{
					std::cerr << "Option without an argument: " << line << std::endl;
					exit(1);
				}
			}
// 			line = parseH.returnUntillStrip( "\n", content );
		}
	}
	else
	{
		std::cerr << "cannot open profile '" << filename << "'" << std::endl;
		exit(1);
	}
}

void Settings::saveProfile()
{
	std::string fulldir;
	fulldir = dirlayout->savedir;
	fulldir.append("/");
	fulldir.append(profileName);

	if ( !dirH.exists(fulldir) )
		dirH.make(fulldir);

	std::stringstream filename;
	filename << fulldir << "/" << profileName << ".pro";

	std::stringstream buf;
	for( cvarit = cvarlist.begin(); cvarit != cvarlist.end(); cvarit++ )
	{
		if ( !cvarit->second->local )
		{
			if ( cvarit->second->type == T_INT )
				buf << cvarit->first << " " << cvarit->second->int_val << std::endl;
			else if ( cvarit->second->type == T_STRING )
				buf << cvarit->first << " " << cvarit->second->string_val << std::endl;
		}
	}

	BeFilesystem::Instance()->save(filename.str(), buf.str());
// 	fileH.save(filename.str(), buf.str());

	std::stringstream msg;
	msg << "Profile saved to: " << filename.str();
	Logbuffer::Instance()->add(msg);
	std::cerr << msg.str() << std::endl;
}

bool Settings::existsCVar( const std::string& sw)
{
	for( cvarit = cvarlist.begin(); cvarit != cvarlist.end(); cvarit++ )
		if ( cvarit->first == sw )
			return true;
	
	return false;
}

void Settings::doCommandLineOptions(int argc, char *argv[])
{
	// check if --help occurs, overrides the rest
	for (int i=1; i < argc; i++ )
	{
		std::string sw = argv[i];
		if ( sw == "--help" )
		{
			createHelpInfo();
			std::cout << helpinfo.str() << std::endl;
			exit(1);
		}
	}

	// decode arguments
	int optind=1;
	while ((optind < argc)) //  && (argv[optind][0]=='-')
	{
		if ( argv[optind][0]=='-' )
		{
			std::string sw = argv[optind];

			if ( sw=="--profile" )
			{
				if ( argv[++optind] )
				{
					loadProfile(argv[optind]);
				}
				else
				{
					std::cerr << "--profile expects a filename" << std::endl;
					exit(1);
				}
			}

			else if ( parseH.beginMatchesStrip( "--", sw ) )
			{
				if ( isCVar(sw) )
				{
					if ( argv[++optind] )
					{
						if ( !setCVar(sw, argv[optind]) )
							exit(1);
					}
					else
					{
						std::cerr << "--" << sw << " expects an argument" << std::endl;
						exit(1);
					}
				}
				else
				{
					std::cerr << "Unknown commandline option: --" << sw << std::endl;
				}
			}
		}
		optind++;
	}
 
	if ( optind < argc )
	{
		std::cerr << "Unknown commandline option: " << argv[optind] << std::endl;
	}
	std::cout << std::endl;
}

Settings::~Settings()
{
	for( cvarit = cvarlist.begin(); cvarit != cvarlist.end(); cvarit++ )
		delete cvarit->second;
}
